Ультразвуковые дальнометры HC-SR04 черезвычайно популярны в любительской робототехнике из-за своей дешевизны и простоты. Я тоже не остался в стороне, когда баловался с Arduino, теперь же хочу разобрать работу с сенсором на "низком уровне" и напсать пример работы на чистом Си.
Есть один принципальный момент. Часто работу с сенсором HC-SR04 реализуют через внешние прерывания. Но. В ATmega8 их всего два, и если сенсор будет работать на колесном шасси, то внешние прерывания будут заняты колесными энкодерами. Однако, как я говорил в посте про прерывание захвата, оно работает аналогично внешнему, т.е. есть смысл попытаться его задействовать.
Заглянем в datasheet HC-SR04 и посмотрим на протокол работы сенсора:
ШИМ штука не сложная, по крайней мере, не сложнее чем мигание светодиодом. Принцип тот же: включаешь - выключаешь, и никаких сложных протоколов с контрольными суммами. В июле у меня был пост про таймер в CTC режиме, там если в прерывании начать переключать GPIO то получится вполне полноценный ШИМ. Такой способ хорош тем, что в ШИМ можно превратить любой свободный пин GPIO. Тогда получается, что частота ШИМ будет равна частоте переполнения счетчика, т.е. для 8-битного таймера: (частота кварца)/(предделитель_таймера * 256). Вобще-то, если ШИМ делать вручную, то частоту можно повысить, если в качестве значения таймера брать остаток от деления, но тогда и разрядность ШИМ соответственно понизится. Пример для двухкратного увеличения: (2 % 127)== (129 % 127) == 2.
Однако, есть в AVR аппаратные ШИМ когда все обходится без прерываний, и работает все с двух строчек кода. Называется он Fast PWM.
Для его настойки пондобится лишь контрольный регистр таймера TCCR2:
при поступлении сигнала на захват,
содержимое регистров TCNT1H:TCNT1L
копируется в регистры ICR1H:ICR1L
Что делать? Справедливости ради замечу, что 4-х пиновые вентиляторы лишены этого недостатка, но сейчас речь не о них. Для 3-х пинового вентилятора можно попробывать сделать "умное" управление.
В 8-см вентиляторе на 3300 RPM, датчик Холла срабатывает 110 раз в секунду. Т.е. он (датчик Холла) работает с периодом в девять миллисекунд. Тогда, если мы на время отключим ШИМ, подадим питание 12 Вольт, замерим длительность одного периода, и затем снова включим ШИМ, то мы получим и управление через ШИМ и достоверный контроль за количеством оборотов. Вентилятор, как и все двигатели, очень инерционные штуки и такой фокус никак не скажется на скорости вентилятора, если его проводить не чаще чем раз в две-три секунд. В остальное время, для простого контроля, чтобы быть уверенным, что вентилятор в принципе крутиться, сгодится подсчет имульсов с датчика Холла и без этого фокуса.
Периоды сигналов в AVR можно измерять с помощью т.н. прерывания по захвату. Основные моменты:
за один оборот, датчик Холла
срабатывает дважды
Обычно внешние прерывания разбирают на кнопках, но у меня есть более интересный пример: счетчик оборотов компьютерного кулера.
Напомню, что подсчет оборотов ведется через датчик Холла, который является неотъемлемой частью компьютерного вентилятора. В данном случае пойдет речь о трех-пиновым вентиляторе. схема подключения взята из одного из предыдущих постов, когда счетчик писался для Arduino. В качестве шаблона кода взят пример, из прерыдущего поста про фоторезистор. Из примера, были уделены три строчки относящиеся к АЦП.
В официальном руководстве на ATmega8, внешние прерывания(External Interrupts) описаны на странице 66. Они имеют более высокий приоритет перед другими прерываниями. Управляются регистром MCUCR:
Попробуем сделать датчик освещености. В комбинации с модулем RTC такой датчик может управлять освещением и подсветкой в системах типа: "умный дом". Датчик будет на фоторезисторе, сопротивление которого обратно пропорционально уровню освещености. Фоторезистор будет подключаться по схеме делителя напряжения, где в верхнее плечо будет установлен постоянный резистор 10K, а в нижнее сам фоторезистор. Нулевая точка будет поключена к пину ADC0 микроконтроллера. По такой же схеме можно подключать терморезистор или клавиатурную сборку на тактовых кнопках, где к каждой кнопке будет подключен резистор с каким-то своим индивидуальным значенем. Тогда, замеряя разность потенциалов, можно будет узнать какая имено кнопка была нажата.
Но вернемся к фоторезистору. Модель котрая мне попалась под руку - VT93N2. Даташит на него представлен ниже:
В одном из предыдущих постов, я рассказывал про подключение термопары к Arduino. Теперь настало время рассмореть подключение к AVR микроконтроллерам на чистом Си, благо, как выяснилось, это совсем не сложно.
Напомню, что термопара подключается через адаптер MAX6675, который в свою очередь подключается к микроконтроллеру через SPI интерфейс.
После I2C, SPI интерфейс скорее всего покажется очень простым. Если расмотреть аппаратную реализацию SPI, то там есть регистр данных SPDR, записью или чтением с которого и осуществляется вся работа c SPI.
Программная реализация, как выяснилось, не намного сложнее, но сначала о том, что такое SPI. Фирменный аппнот к SPI можо найти на сайте Atmel: AVR151: Setup And Use of The SPI
рассмотрим схему подключения взятую оттуда:
После UART, реализация TWI модуля на AVR кажется довольно корявой и без понимания I2C протокола будет не просто его освоить. Однако, если разобраться с "софтовой" эмуляцией протокола, то работа с TWI модулем уже не составит труда.
Не последне место в этом списке должно быть у официального аппнота: AVR315: Using the TWI module as I2C master
Если открыть руководство для ATmega8, на странице 156 раздел "Two-Wire Serial Interface", то там даже будет примерный набросок программы для работы с TWI:
Функция форматного вывода printf довольно мощное средство языка Си. Приминительно к микроконтроллерам, возможно, сначала не совсем понятно, что там считать за стандарный поток вывода и как здесь может работать printf(). Библиотека avr-libc содержит файл stdio.h которая включает реализацию стандарных функций ввода-вввода.
<stdio.h>: Standard IO facilities
Я взял оттуда ХеллоВорлд:
#include <stdio.h> static int uart_putchar(char c, FILE *stream); static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE); static int uart_putchar(char c, FILE *stream) { if (c == '\n') uart_putchar('\r', stream); loop_until_bit_is_set(UCSRA, UDRE); UDR = c; return 0; } int main(void) { init_uart(); stdout = &mystdout; printf("Hello, world!\n"); return 0; }
И модифицировал пример из поста ATmega8: работа на Си с USART/UART через прерывание заменив функцию writeSerial(char* str) на printf. Получилась такая штука:
Небольшая демо-программа, таймер для ATmega8 с дисплеем HD44780. В качестве отправной точки использовал исходник из предыдущего поста. Идея была почерпнута здесь: LCD "Bigfont" Numbers over 2 or 4 lines".
Исходник:
Управление ЖК-дисплеем HD44780 довольно простое. Он имеет параллельную шину на 8 пин и три управляющих линии.